package biz.innovasoft.nodb.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.prevayler.Prevayler;

import biz.innovasoft.nodb.UpdateableList;
import biz.innovasoft.nodb.query.Tuple;
import biz.innovasoft.nodb.transaction.UpdateUpdateableListTransaction;

public class ArrayUpdateableList<E> implements UpdateableList<E> {

	private static final long serialVersionUID = 1L;
	
	private Prevayler prevayler;
	private List<Tuple> tuples;
	
	public ArrayUpdateableList (Prevayler prevayler, List<Tuple> tuples) {
		this.prevayler = prevayler;
		this.tuples = tuples;
	}

	public void clear() {
		tuples.clear();
	}

	public boolean contains(Object o) {
		return tuples.contains(o);
	}

	public boolean containsAll(Collection<?> c) {
		return tuples.containsAll(c);
	}

	public boolean equals(Object o) {
		return tuples.equals(o);
	}

	@SuppressWarnings("unchecked")
	public E get(int index) {
		return (E)tuples.get(index).value;
	}

	public int hashCode() {
		return tuples.hashCode();
	}

	public int indexOf(Object o) {
		return tuples.indexOf(o);
	}

	public boolean isEmpty() {
		return tuples.isEmpty();
	}

	public Iterator<E> iterator() {
		return new UIterator<E>(tuples.iterator());
	}

	public int lastIndexOf(Object o) {
		return tuples.lastIndexOf(o);
	}

	public ListIterator<E> listIterator() {
		return new UListIterator<E> (tuples.listIterator());
	}

	public ListIterator<E> listIterator(int index) {
		return new UListIterator<E> (tuples.listIterator(index));
	}

	@SuppressWarnings("unchecked")
	public E remove(int index) {
		Tuple tuple = tuples.get(index);
		if (tuple.oid == 0)
			return (E)tuples.remove(index);
		tuple.deleted = true;
		return (E)tuple.value;
	}

	public boolean remove(Object o) {
		for (Tuple tuple : tuples ) {
			if ( tuple.value.equals(o) ) {
				if (tuple.oid == 0) 
						return tuples.remove(tuple);
				tuple.deleted = true;
				return true;
			}
		}
		return false;
	}

	public boolean removeAll(Collection<?> c) {
		throw new UnsupportedOperationException ();
	}

	public boolean retainAll(Collection<?> c) {
		throw new UnsupportedOperationException ();
	}

	public E set(int index, E element) {
		throw new UnsupportedOperationException ();
	}

	public int size() {
		return tuples.size();
	}

	@SuppressWarnings("unchecked")
	public List<E> subList(int fromIndex, int toIndex) {
		List<E> list = new ArrayList<E>();
		for (int t=fromIndex; t < toIndex; t++) {
			list.add((E)tuples.get(t).value);
		}
		return list;//tuples.subList(fromIndex, toIndex);
	}

	@SuppressWarnings("unchecked")
	public Object[] toArray() {
		List<E> list = new ArrayList<E>();
		for (Tuple tuple : tuples) {
			list.add((E)tuple.value);
		}
		return list.toArray();
	}

	@SuppressWarnings("unchecked")
	public <T> T[] toArray(T[] a) {
		List<E> list = new ArrayList<E>();
		for (Tuple tuple : tuples) {
			list.add((E)tuple.value);
		}
		return list.toArray(a);
	}
	
	@Override
	public List<Tuple> asListOfTuples () {
		return this.tuples;
	}
	
	@Override
	public void commit() {
		prevayler.execute(new UpdateUpdateableListTransaction (tuples));
	}

	@Override
	public boolean add(E e) {
		return tuples.add(new Tuple (0, e));
	}

	@Override
	public void add(int index, E element) {
		tuples.add (index, new Tuple (0, element));
	}

	@Override
	public boolean addAll(Collection<? extends E> c) {
		List<Tuple> coll = new ArrayList<Tuple>();
		for (E obj : c) {
			coll.add(new Tuple(0, obj));
		}
		return tuples.addAll(coll);
	}

	@Override
	public boolean addAll(int index, Collection<? extends E> c) {
		List<Tuple> coll = new ArrayList<Tuple>();
		for (E obj : c) {
			coll.add(new Tuple(0, obj));
		}
		return tuples.addAll(index, coll);
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<E> getObjectsToStore() {
		List<E>added = new ArrayList<E>();
		for (Tuple tuple : tuples) {
			if (tuple.oid == 0)
				added.add((E)tuple.value);
		}
		return added;
	}

	@Override
	public List<Long> getObjectsToDelete() {
		List<Long>removed = new ArrayList<Long>();
		for (Tuple tuple : tuples) {
			if (tuple.deleted)
				removed.add(tuple.oid);
		}
		return removed;
	}
	
	@Override
	public List<Tuple> getObjectsToUpdate () {
		List<Tuple>update = new ArrayList<Tuple>();
		for (Tuple tuple : tuples) {
			if (!tuple.deleted && tuple.oid != 0)
				update.add(tuple);
		}
		return update;
	}
	
	private class UIterator<T> implements Iterator<T> {

		private Iterator<Tuple> ituples;
		
		public UIterator (Iterator<Tuple> ituples) {
			this.ituples = ituples;
		}
		
		@Override
		public boolean hasNext() {
			return ituples.hasNext();
		}

		@SuppressWarnings("unchecked")
		@Override
		public T next() {
			Tuple tuple = ituples.next();
			return (T)tuple.value;
		}

		@Override
		public void remove() {
			ituples.remove();
		}
		
	}
	
	private class UListIterator<T> implements ListIterator<T> {

		private ListIterator<Tuple> tuples;
		
		public UListIterator (ListIterator<Tuple> tuples) {
			this.tuples = tuples;
		}
		
		@Override
		public void add(T e) {
			//tuples.add (new Tuple (0, e));
			throw new UnsupportedOperationException ();
		}

		@Override
		public boolean hasNext() {
			return tuples.hasNext();
		}

		@Override
		public boolean hasPrevious() {
			return tuples.hasPrevious();
		}

		@SuppressWarnings("unchecked")
		@Override
		public T next() {
			return (T)tuples.next().value;
		}

		@Override
		public int nextIndex() {
			return tuples.nextIndex();
		}

		@SuppressWarnings("unchecked")
		@Override
		public T previous() {
			return (T)tuples.previous().value;
		}

		@Override
		public int previousIndex() {
			return tuples.previousIndex();
		}

		@Override
		public void remove() {
			//tuples.remove();
			throw new UnsupportedOperationException ();
		}

		@Override
		public void set(T e) {
			//tuples.set(new Tuple (0, e));
			throw new UnsupportedOperationException ();
		}
	}
	
}
